package com.upcontrol.uartagent.core;

import android.content.Context;

import com.csjbot.csjbase.log.Csjlogger;
import com.csjbot.snowbot_rogue.Events.HWTestEvent;
import com.csjbot.snowbot_rogue.utils.NetDataTypeTransform;
import com.upcontrol.uartagent.constant.ClientConstant;
import com.upcontrol.uartagent.core.inter.ActionListener;
import com.upcontrol.uartagent.core.inter.DataReceive;
import com.upcontrol.uartagent.core.inter.RequestListener;
import com.upcontrol.uartagent.core.util.Error;
import com.upcontrol.uartagent.core.util.PacketUtil;
import com.upcontrol.uartagent.entity.UartPacket;
import com.usb2uartagent.EmUartAgentType;
import com.usb2uartagent.IUARTConnectorHelper;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Copyright (c) 2016, SuZhou CsjBot. All Rights Reserved. <br>
 * www.csjbot.com<br>
 * <p>
 * Created by 浦耀宗 at 2016/11/07 0007-19:19.<br>
 * Email: puyz@csjbot.com<br>
 * <p>
 * RosConntor的事件封装层，处理RosConnector的各种数据，承上启下
 */
public class ClientManager implements DataReceive {
    private static ClientManager ourInstance = new ClientManager();

    public static ClientManager getInstance() {
        return ourInstance;
    }

    /**
     * 发送池，所有的 PacketEntity 进过包装之后都会丢在这里，按照先进先出的顺序来操作
     * 有两个线程来操作，所以这里的操作都是要加同步来实现
     */
    private final ArrayList<PacketEntity> mSendPool = new ArrayList<>();
//    private BlockingDeque<PacketEntity>

    private ActionListener mListener = null;

    /**
     * 主要的连接线程
     */
    private ExecutorService mMainExecutor;
    private RequestListener mRequestListener;
    /**
     * 发送的线程
     */
    private Thread mSendThread;
    private boolean mIsRunning = false;
    private IUARTConnectorHelper mUARTConnectorHelper;

    private ClientManager() {
    }

    @Override
    public void onReceive(byte[] data) {
        UartPacket packet = PacketUtil.parse(data);
        if (packet != null) {
            mRequestListener.onReqeust(packet);
            EventBus.getDefault().post(new HWTestEvent("收到上身板数据 " + NetDataTypeTransform.dumpHex(data)));
        }
    }

    /**
     * 初始化Manager
     *
     * @param listener 成功失败的监听器
     */
    public void init(final Context context, EmUartAgentType emUartAgentType, final String device, final int baudrate, final ActionListener listener) {
        mIsRunning = true;
        mListener = listener;
        if (emUartAgentType == EmUartAgentType.emNormalUartagent) {
            mUARTConnectorHelper = new UartConnectHelper();
        } else {
            mUARTConnectorHelper = new Usb2UartUpControlHelper();
        }

        mSendThread = new Thread(new Runnable() {
            @Override
            public void run() {
                processSendPool();
            }
        }, "UartClientSendThread");
        mSendThread.setDaemon(true);

        mMainExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread ret = new Thread(r, "mMainExecutor");
                ret.setDaemon(true);
                return ret;
            }
        });

        mMainExecutor.submit(new Runnable() {
            @Override
            public void run() {
                if (mUARTConnectorHelper.init(context, device, baudrate) == 0) {
                    mListener.onSuccess();
                    mUARTConnectorHelper.setDataReceive(ClientManager.this);
                    mSendThread.start();
                } else {
                    mListener.onFailed(ClientConstant.EVENT_CONNECT_FAIL);
                }
            }
        });
    }

    /**
     * 把packet 封装成 PacketEntity，加入一个自己封装的Blocking Queue
     *
     * @param packet   所要发送的数据包
     * @param listener 成功或者失败的监听器
     */
    public void sendRequest(final UartPacket packet, final ActionListener listener) {
        mMainExecutor.execute(new Runnable() {
            @Override
            public void run() {
                PacketEntity entity = new PacketEntity(packet, listener);
                addToSendPool(entity);
            }
        });
    }

    public void setRequestListener(RequestListener listener) {
        mRequestListener = listener;
    }

    private void addToSendPool(PacketEntity packetWrapper) {
        synchronized (mSendPool) {
            mSendPool.add(packetWrapper);
            mSendPool.notify();
        }
    }

    /**
     * 发送池
     */
    private void processSendPool() {
        while (mIsRunning) {
            synchronized (mSendPool) {
                Iterator<PacketEntity> iterator = mSendPool.iterator();
                while (iterator.hasNext()) {
                    PacketEntity entity = iterator.next();
                    int stat = mUARTConnectorHelper.send(entity.packet.encodeBytes());
//
                    iterator.remove();

                    if (stat == Error.Uart.SEND_SUCCESS) {
                        entity.onSuccess();
//                        continue;
                    } else {
                        entity.onFailed(stat);
                    }

                }

                try {
                    mSendPool.wait();
                } catch (InterruptedException e) {
                    //ignore
                }
            }
        }
    }

    public void destroy() {
        mMainExecutor.shutdown();
        mIsRunning = false;
        mUARTConnectorHelper.destroy();
    }

    /**
     * MessagePacket 的简单封装，把  MessagePacket 和 ActionListener 封装到了一起，便于处理
     */
    static class PacketEntity {
        UartPacket packet = null;
        ActionListener callback = null;

        public PacketEntity(UartPacket packet, ActionListener callback) {
            this.packet = packet;
            this.callback = callback;
        }

        void onSuccess() {
            if (callback != null) {
                callback.onSuccess();
            }
        }

        void onFailed(int error) {
            if (callback != null) {
                callback.onFailed(error);
            }
        }
    }
}

